package gu.sql2java.geometry;

import static com.google.common.base.Preconditions.checkNotNull;

import java.nio.ByteBuffer;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Set;

import com.google.common.collect.Sets;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.io.ByteOrderValues;
import com.vividsolutions.jts.io.ParseException;
import com.vividsolutions.jts.io.WKBReader;
import com.vividsolutions.jts.io.WKBWriter;

/**
 * MySQL存储格式WKB,WKT与Geometry对象之间的互相转换实现
 * @author guyadong
 * @since 3.18.0
 */
public class MysqlGeometryDataCodec extends GeometryDataCodec {
    public static final MysqlGeometryDataCodec INSTANCE = new MysqlGeometryDataCodec();
    private static final Set<String> GEO_TYPES = Collections.unmodifiableSet(Sets.newLinkedHashSet(Arrays.asList(
            "GEOMETRY","POINT","LINESTRING","POLYGON","MULTIPOINT","MULTILINESTRING",
            "MULTIPOLYGON","GEOMETRYCOLLECTION")));

    public MysqlGeometryDataCodec() {
    }
    @Override
    public Set<String> getGeomtyColumnTypes(){
        return GEO_TYPES;
    }
    @Override
    public Geometry fromWKB(byte[] binary) throws ParseException {
        if(null == binary) {
          return null;
        }
        if(binary.length < 25) {
            throw new ParseException("INVALID binary data length,more than 25 bytes required");
        }
        int srid = ByteBuffer.wrap(binary,0,4).asIntBuffer().get();
        WKBReader wkbReader = new WKBReader();
        Geometry geo = wkbReader.read(Arrays.copyOfRange(binary, 4, binary.length));
        geo.setSRID(srid);
        return geo;
    }
    @Override
    public <T extends Geometry>byte[] toWKB(T input)  {
        if(null == input) {
            return null;  
          }
        WKBWriter wkbWriter = new WKBWriter(2, ByteOrderValues.LITTLE_ENDIAN);
        byte[] binary = wkbWriter.write(input);
        byte[] out = new byte[4 + binary.length];
        /** 写入SRID */
        ByteBuffer.wrap(out).asIntBuffer().put(input.getSRID());
        System.arraycopy(binary, 0, out, 4, binary.length);
        return out;
    }
    @Override
    public final Object readGeometryData(ResultSet rs, int columnIndex) throws SQLException {
        try {
            return fromWKB(checkNotNull(rs,"rs is null").getObject(columnIndex));
        } catch (ParseException e) {
            throw new SQLException(e);
        }
    }
}
